package com.dodo.blog.ui.component.repeater;

import com.dodo.blog.ui.ajax.AjaxListener;
import com.dodo.blog.ui.ajax.AjaxListenerHelper;
import com.dodo.blog.ui.ajax.IAjaxRequestHandler;
import com.dodo.blog.ui.ajax.RepeaterAjaxRequestHandler;
import com.dodo.blog.ui.component.Component;
import com.dodo.blog.ui.component.container.Container;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.List;

/**
 * @author <a href="mailto:pohorelec@comvai.com">Jozef Pohorelec</a>
 */
public abstract class Repeater<T>
        extends AbstractRepeater<T>
        implements AjaxListener
{
    private static final long serialVersionUID = 1L;

    public static final int DEFAULT_MAX_ROWS = 5;

    public static final String ATTR_REPEATER_ID = "rid";

    public static final String CLASS_REPEATER_LINK = "__repeater-link";

    public static final String PARAM_FIRST_ROW = "__p_fr";

    public static final String PARAM_MAX_ROWS = "__p_mr";

    public static final String PARAM_ORDER_BY = "__p_ob";

    public static final String PARAM_ORDER_DIRECTION = "__p_od";

    private static final String URL = "{0}?" +
            PARAM_ID + "={1}&" +
            PARAM_FIRST_ROW + "={2}&" +
            PARAM_MAX_ROWS + "={3}&" +
            PARAM_ORDER_BY + "={4}&" +
            PARAM_ORDER_DIRECTION + "={5}&" +
            IAjaxRequestHandler.PARAM_AJAX_HANDLER + "={6}";

    private IDataSource<T> dataSource;

    private int maxRows = DEFAULT_MAX_ROWS;

    private int firstRow = 0;

    private String orderBy;

    private OrderDirection orderDirection;

    private Paginator<T> paginator;

    private Tag tag;

    protected Container container;

    public Repeater( String id, IDataSource<T> dataSource )
    {
        this( id, dataSource, Tag.DIV );
    }

    public Repeater( String id, IDataSource<T> dataSource, Tag tag )
    {
        super( Tag.DIV );
        create( id, dataSource, tag );
    }

    protected void create( String id, IDataSource<T> dataSource, Tag tag )
    {
        init( id, dataSource, tag );
        process();
        populateRows();
    }

    protected void init( String id, IDataSource<T> dataSource, Tag tag )
    {
        clear();

        this.tag = tag;
        this.dataSource = dataSource;
        setId( id );

        paginator = new Paginator<T>( getId() );
        super.add( paginator );

        container = new Container( tag );
        super.add( container );

        AjaxListenerHelper.addListener( this );
    }

    @SuppressWarnings( value = "unchecked" )
    protected void process()
    {
        processRequest();
        Repeater<T> repeater = ( Repeater<T> ) AjaxListenerHelper.getListener( this );
        firstRow = repeater.getFirstRow();
        maxRows = repeater.getMaxRows();
        orderBy = repeater.getOrderBy();
        orderDirection = repeater.getOrderDirection();
    }

    protected void populateRows()
    {
        List<T> rows = getRows();

        for ( T row : rows )
        {
            populate( row );
        }
    }

    @Override
    @SuppressWarnings( value = "unchecked" )
    public List<T> getRows()
    {
        List<T> result = dataSource.getRows( firstRow, maxRows + 1, orderBy, orderDirection );

        if ( result == null )
        {
            throw new NullPointerException( "Data source cannot return null value!" );
        }

        if ( result.size() > 0 )
        {
            if ( firstRow == 0 )
            {
                paginator.getFirstPage().setHref( "" );
                paginator.getFirstPage().setDisabled();

                paginator.getPrevPage().setHref( "" );
                paginator.getPrevPage().setDisabled();
            }
            else
            {
                paginator.getFirstPage().setHref( createUrl( 0, maxRows, orderBy, orderDirection ) );
                paginator.getPrevPage().setHref( createUrl( firstRow - maxRows, maxRows, orderBy, orderDirection ) );
            }

            if ( result.size() <= maxRows )
            {
                paginator.getNextPage().setHref( "" );
                paginator.getNextPage().setDisabled();
            }
            else
            {
                paginator.getNextPage().setHref( createUrl( firstRow + maxRows, maxRows, orderBy, orderDirection ) );
            }

            paginator.getPageIndicator().set( firstRow + 1, result.size() - 1 == maxRows ? firstRow + maxRows : firstRow + result.size() );

            if ( result.size() > maxRows )
            {
                result = result.subList( 0, result.size() - 1 );
            }
        }
        else if ( firstRow > 0 )
        {
            firstRow = firstRow - maxRows;
            if ( firstRow < 0 )
            {
                firstRow = 0;
            }
            AjaxListenerHelper.setListener( this );
            return getRows();
        }
        else
        {
            paginator.getFirstPage().setHref( "" );
            paginator.getFirstPage().setDisabled();

            paginator.getPrevPage().setHref( "" );
            paginator.getPrevPage().setDisabled();

            paginator.getNextPage().setHref( "" );
            paginator.getNextPage().setDisabled();

            paginator.getPageIndicator().set( 0, 0 );
        }

        return result;
    }

    @SuppressWarnings( value = "unchecked" )
    protected String createUrl( Integer firstRow, Integer maxRows, String orderBy, OrderDirection orderDirection )
    {
        return MessageFormat.format( URL, getRequest().getRequestURI(), getUniqueId(), firstRow, maxRows, orderBy != null ? orderBy : "", orderDirection != null ? orderDirection.name() : "", RepeaterAjaxRequestHandler.class.getName() );
    }

    @SuppressWarnings( value = "unchecked" )
    private void processRequest()
    {
        String repeaterId = getRequest().getParameter( PARAM_ID );
        if ( getUniqueId().equals( repeaterId ) )
        {
            // first row
            String pfr = getRequest().getParameter( PARAM_FIRST_ROW );
            Integer fr = null;
            try
            {
                fr = Integer.valueOf( pfr );
            }
            catch ( NumberFormatException e )
            {
                // ignore
            }

            // max rows
            String pmr = getRequest().getParameter( PARAM_MAX_ROWS );
            Integer mr = null;
            try
            {
                mr = Integer.valueOf( pmr );
            }
            catch ( NumberFormatException e )
            {
                // ignore
            }

            // order by
            String ob = getRequest().getParameter( PARAM_ORDER_BY );

            // order direction
            String pod = getRequest().getParameter( PARAM_ORDER_DIRECTION );
            OrderDirection od = null;
            try
            {
                od = OrderDirection.valueOf( pod );
            }
            catch ( IllegalArgumentException e )
            {
                // ignore
            }

            // update repeater into session
            firstRow = fr != null ? fr : firstRow;
            maxRows = mr != null ? mr : maxRows;
            orderBy = ob != null || ob.trim().length() > 0 ? ob : orderBy;
            orderDirection = od != null ? od : orderDirection;

            AjaxListenerHelper.setListener( this );
        }
    }

    @Override
    public void add( Component component )
    {
        container.add( component );
    }

    public IDataSource<T> getDataSource()
    {
        return dataSource;
    }

    public int getMaxRows()
    {
        return maxRows;
    }

    public void setMaxRows( int maxRows )
    {
        this.maxRows = maxRows;
    }

    public int getFirstRow()
    {
        return firstRow;
    }

    public void setFirstRow( int firstRow )
    {
        this.firstRow = firstRow;
    }

    public String getOrderBy()
    {
        return orderBy;
    }

    public void setOrderBy( String orderBy )
    {
        this.orderBy = orderBy;
    }

    public OrderDirection getOrderDirection()
    {
        return orderDirection;
    }

    public void setOrderDirection( OrderDirection orderDirection )
    {
        this.orderDirection = orderDirection;
    }

    public Paginator<T> getPaginator()
    {
        return paginator;
    }

    public void setPaginator( Paginator<T> paginator )
    {
        this.paginator = paginator;
    }

    public Tag getTag()
    {
        return tag;
    }

    @SuppressWarnings( value = "unchecked" )
    public String renderAjax( HttpServletRequest request )
    {
        Repeater<T> repeater = ( Repeater<T> ) AjaxListenerHelper.getListener( this );
        repeater.create( repeater.getId(), repeater.getDataSource(), repeater.getTag() );

        return repeater.render().toString();
    }

    @Override
    public String getUniqueId()
    {
        return getId();
    }
}